//
// Created by gaodapeng on 2020/4/12.
//
#include <stdio.h>
#include <stdlib.h>

/**
 * 指针的基础
 * 在非初始化左表达式的基础上，*符号是指向某地址的内容,即取某指针指的地址中的值 &符号是取值对应的地址
 */
void ptr1() {
    //指针：最核心的内容
    // int float char 指针数据类型
    //int i=10 10数值
    //int *ptr_i = &i; ptr_i存的是地址
    //指针是一种特殊的变量（指针变量），它保存的是地址，是另一个变量的内存地址

    int i = 258;
    int *p_i = &i;//地址是i的值

    printf("%p\n", &i);
    printf("%p\n", p_i);
    printf("p_i自己的地址%p\n", &p_i);//指针本身也是一种变量类型，它也有自己的地址
    printf("int*的大小%lu\n", sizeof(int *));//指针的大小跟系统有关，32位的系统是4个字节，而64位系统的占8个字节
    printf("char*的大小%lu\n", sizeof(char *));
    printf("longlong*的大小%lu\n", sizeof(long long *));
    printf("void*的大小%lu\n", sizeof(void *));//void*是一种特殊的指针，其他类型的指针都可以赋值给void*
    void *p_v = p_i;
    printf("p_v%p\n", p_v);
}


/**
 *
 * 指针的赋值，释放
 * malloc
 * free
 *
 */
void ptr2() {
    int i = 258;
    int j = 10000;
    int *p_i = &i;

    printf("%p\n", &i);
    printf("%p\n", &j);
    printf("p_i自己的地址%p\n", &p_i);//输出可知，这三个地址是分配在栈上的，指针本身的大小为12字节（为何？）

    printf("============\n");
    p_i = (int *) malloc(sizeof(int));//使用malloc函数在堆上分配了一块内存给p_i
    printf("p_i自己的地址%p\n", &p_i);
    printf("p_i=%p\n", p_i);
    free(p_i);
    p_i = NULL;//释放完成指针后，必须再将其设置为NULL不然会变成悬空指针。和野指针（随机，无限制的）一样的危险，必须避免
    //指针设置成null后，会成0。0这个地址，一般不能被访问
    printf("p_i=%p\n", p_i);

    //  总结：
    //  &运算符，用于取一个对象的地址
    //  *是一个间接寻址符，用于访问指针所指向的地址的值.（初始化时不算）
    //  int *p 定义一个指针
    //  int b = *p 用于访问指针所指向的地址的值
    //  & 与* 是一对互逆运算符

    int var = 222;
    int *p = &var;
    int temp = *p;//注意区别于temp = p
    printf("%d,%d,%d", var, *p, temp);//输出值一样的

}

void swapInt(int *x, int *y);

/**
 * 指针置换
 */
void ptr3() {
    int a = 20;
    int b = 30;

    printf("a=%d,b=%d\n", a, b);
    swapInt(&a, &b);
    printf("a=%d,b=%d\n", a, b);
}

void swapInt(int *x, int *y) {
    int temp = *x;
    *x = *y;
    *y = temp;
    printf("x=%d,y=%d\n", *x, *y);
}

/**
 * 指针的转换
 */
void ptr4(void) {
    //空指针
    int *p_i = NULL;//给初始化指针赋值为NULL，free之后，置为NULL。避免野指针或悬空指针
    //通用指针 void*
    int n;
    char c;
    short s;
    //指针类型分别是int，char，short
    int *p_n = &n;
    char *p_c = &c;
    short *p_s = &s;

    printf("p_n=%p\n", p_n);
    printf("p_c=%p\n", p_c);
    printf("p_s=%p\n", p_s);

    void *p_void = p_n;//把一个整型的指针转换成一个通用指针，地址不会变，但是类型信息丢失
    printf("p_void=%p\n", p_void);

    int *p_n_1 = (int *) p_void;
    printf("p_n_1=%p\n", p_n_1);

    double *p_double = (double *) p_n;//语法正确，存在语义错误，编译器无法识别
    printf("p_double=%p\n", p_double);


}


/**
 * 指针的算数运算
 *
 * 对于变量，不同的类型，占用的空间大小不一样
 * 对于不同变量的指针变量，他们占用的空间都是相同的
 */
void ptr5() {
    int i = 100;
    int *p_i = &i;
    printf("p_i=%p\n", p_i);
    printf("p_i+1=%p\n", p_i + 1);//int指针每+1，都是加4位的
    printf("p_i+2=%p\n", p_i + 2);
    printf("p_i+3=%p\n", p_i + 3);

    short c = 100;
    short *p_c = &c;
    printf("p_c=%p\n", p_c);
    printf("p_c+1=%p\n", p_c + 1);//short指针，每次+1，都是加2位的
    printf("p_c+2=%p\n", p_c + 2);
    printf("p_c+3=%p\n", p_c + 3);
    printf("p_c+3=%p\n", p_c + 3);

    int r = 12;
    void *p_r = &r;
    printf("p_r=%p\n", p_r);
    printf("p_r+1=%p\n", p_r + 1);
    printf("p_r+2=%p\n", p_r + 2);//void*进行计算时，每次步长是1

    //结论：指针进行运算的时候，步长跟所指的类型有关。int就是4字节，short就是两字节 指针的数值每+1，就代表了一个字节
    //指针只能进行加法和减法，或者自增自减


}

/**
 * 指针与数组
 * 数组名就是数值的起始地址，也是第一个元素的地址，数组名就是一个常量指针
 */
void ptr6(void) {
    int array[] = {1, 2, 3, 4, 5};
    int *p_array = array;
    printf("array=%p\n", array);
    printf("&array=%p\n", &array);//数组加不加&符号都行,数组名称就是一个指针
    printf("array[0]=%p\n", &array[0]);//数组地址就是数组的首元素的地址
    printf("p_array=%p\n", p_array);//数组就是一个指针，可以直接赋值给指针
    printf("\n");

    printf("p_array[2]=%d\n", p_array[2]);//指针在接到了数组的地址之后，也可以进行数组操作
    printf("\n");

    p_array[2] = 20;
    *(p_array + 3) = 200;
    printf("p_array[2]=%d\n", p_array[2]);
    printf("p_array[3]=%d\n", p_array[3]);
    printf("p_array+3=%d\n", *(p_array + 3));
    printf("*(array+3)=%d\n", *(array + 3));
    printf("\n");

    //*取值，&取址。
    //array+3,&array[3],p_array+3,&p_array[3]
    printf("array+3=%p\n", array + 3);//array偏移3个步长
    printf("&array[3]=%p\n", &array[3]);//array偏移3个步长后取值
    printf("p_array+3=%p\n", p_array + 3);//p_array 偏移3个步长的
    printf("&p_array[3]=%p\n", &p_array[3]);//p_array取值第三个的地址

    printf("\n");
    //*(array+3),*&array[3],*(p_array+3),*&p_array[3]代表的都是array[3]
    printf("*(array+3)=%d\n", *(array + 3));
    printf("*&array[3]=%d\n", *&array[3]);
    printf("*(p_array+3)=%d\n", *(p_array + 3));
    printf("*&p_array[3]=%d\n", *&p_array[3]);
    printf("\n");

    //自增
    p_array += 2;
    printf("p_array=%p\n", p_array);
    printf("value=%d\n", *p_array);//这里就已经偏移到了第三个元素
}

/**
 * 数组指针与指针数组
 * 优先级 * [] (),    () > [] > *
 * int (*p_array)[5]数组指针，指以指定数组长度（这里是方括号中的5）为步长的指针
 * int *p_array[5]指针数组 => *(p_array[5])
 */
void ptr7(void) {
    int array[] = {1, 2, 3, 4, 5};
    int *p_array = array;
    printf("array=%p\n", array);
    printf("&array=%p\n", &array);
    printf("\n");
    printf("array+1=%p\n", array + 1);
    printf("&array+1=%p\n", (&array) + 1);
    printf("&array+2=%p\n", (&array) + 2);
    //&array代表了整个数组的地址，在这个基础上算偏移，步长为array.length * sizeof(int)

    int (*p_array2)[5] = &array;//数组指针.很明显如果p_array2+1的话，步长是array.length*4（int的字节数）
    printf("p_array2=%p\n", p_array2);
    printf("p_array2+1=%p\n", p_array2 + 1);
    int *p_array3 = &array;//这个是普通指针，不是数组指针。不具有步长为array.length*4的特性
    printf("p_array3+1=%p\n", p_array3 + 1);
    int (*p_array4)[4] = &array;//数组指针，但是步长只有4*4
    printf("p_array4=%p\n", p_array4);
    printf("p_array4+1=%p\n", p_array4 + 1);

}


void ptr8() {
    int array[] = {1, 2, 3, 4, 5};
    int (*p_array)[5] = &array;
    for (int i = 0; i < 5; ++i) {
        printf("array[%d]=%d, *(array+%d)=%d\n", i, array[i], i, *(array + i));
        printf("&array[%d]=%p, array+%d=%p\n", i, &(array[i]), i, array + i);
    }

    printf("\n");

    int *p = NULL;
    //通过指针遍历
    for (p = array; p < array + 5; p++) {
        printf("p=%p,*p=%d\n", p, *p);
    }
}

void ptr9() {

    int a[5] = {1, 2, 3, 4, 5};
    int *ptr = (int *) (&a + 1);//数组的指针+1，步长是数组的长度，所以ptr指向的是数组a之后的一个位置，由于转换成了int指针，所以其步长是一个int值的步长，也就是4位
    printf("%d %d\n", *(a + 1), *(ptr - 1));//a+1是a[1]，取值是2，后面一个取值是5
    //输出预估是2，5

}


/**
 sizeof 是统计字节数的
 */
void ptr10() {
    int array[5] = {0};
    printf("sizeof(int*)=%lu\n", sizeof(int *));//8
    printf("sizeof(array[0])=%lu\n", sizeof(&array[0]));//8
    printf("sizeof(array)=%lu\n", sizeof(array));//20
    printf("sizeof(array)=%lu\n", sizeof(&array));//8
}


/**
 指针和二维数组
 */
void ptr11(void) {
    int array[5] = {0};

    int arr[][3] = {
            {1,  2,  3},
            {4,  5,  6},
            {7,  8,  9},
            {10, 11, 12}
    };
    int (*p_a)[3] = arr;//数组指针
    //遍历数组
    //下标访问
    for (int i = 0; i < 4; i++) {
        for (int j = 0; j < 3; j++) {
            printf("a[%d][%d]=%d\n", i, j, arr[i][j]);
            printf("p_a[%d][%d]=%d\n", i, j, p_a[i][j]);
        }
    }

    //指针偏移访问
    // * 星号可以让数组指针变成数值指针，就跟数组指针初始化的时候使用&与号是相对的含义
    for (int k = 0; k < 4; k++) {
        for (int l = 0; l < 3; l++) {
            printf("*(*(arr+%d)+%d)=%d\n", k, l, *(*(arr + k) + l));
            printf("*(*(p_a+%d)+%d)=%d\n", k, l, *(*(p_a + k) + l));
        }
    }

    //下标和指针偏移
    //由此可得，对于二维数组a[][], a[2]并非是数组指针而是第三行的首地址
    for (int m = 0; m < 4; m++) {
        for (int n = 0; n < 3; n++) {
            printf("*(arr[%d] + %d)=%d\n", m, n, *(arr[m] + n));
            printf("*(p_a[%d] + %d)=%d\n", m, n, *(p_a[m] + n));
            printf("*(p_a + %d)[%d]=%d\n", m, n, *((p_a + m)[n]));//这个写法和上边的写法不对应,事实上这个写法是不对的
        }
    }

    //这里的**也是可以取出值的，第一个*取出指针，第二个*取出值
    printf("**(arr+2)=%d", **(arr + 2));
}

/**
 * 指针数组和数组指针的区别
 *
 * 指针数组：int *x[]
 * 数组指针：int (*p_x)[5]
 *
 * 涉及知识：字符串就是char的数组，获取它时，得到的是指针
 *
 * 另外：指针数组读取的是常量区的值，所以无法修改
 */
void ptr12(void) {

    char *x = "1234";//字符串可以用char指针引用
    puts(x);
    printf("%p\n", x);
    printf("%p\n", &x);
    printf("%s\n", x);

    char *color[] = {"red", "green", "blue"};//指针数组，保存的是字符串的地址
    for (int i = 0; i < 3; ++i) {
        puts(color[i]);
        printf("color[%d] = %p\n", i, color[i]);
        color[i] = "color";
//        *color[i] = "ref";//这种赋值方式是不可以的，因为指针数组指向了常量区
//        *color[i] = 'f';//所以其内容不可更改，不过指针本身指向的地址可以修改
        printf("color[%d] = %p\n", i, color[i]);
    }
    printf("color length = %lu\n", sizeof(color[0]));//输出8，因为color里面存的是指针

    char col[][6] = {"red", "green", "blue"};
    char (*pcol)[6] = col;//数组指针，对应二维数组的一个行
    for (int j = 0; j < 3; ++j) {
        puts(pcol[j]);//pcol[j]表示一个行，即"red"等，j表示行的取值
        pcol[j][0] = 'a';//数组指针是可以进行值的修改的，与指针数组不同。因为其就是把内容放在数组里面了
        puts(pcol + j);//同上，表示行的选取
    }
    printf("color length = %lu\n", sizeof(pcol[0]));//输出6，因为数组指针指向的地址里面存的还是值
}

/**
 * 查找某字符在资源中的源地址
 * 知识点：二级指针：可以用来初始化一级指针
 *
 * @param src 资源的内容
 * @param ch 查找的内容
 * @param result 查找出来的结果
 */
void find(const char *src, char ch, char **result) {
    char *c = src;
    printf("%lu", sizeof(src));//sizeof不能用于输出数组的大小，只能输出数组指针的指针大小

    for (int i = 0; i < 13; ++i) {
        if (src[i] == ch) {
            *result = &c[i];
        }
    }
}

/**
 * 指针的指针
 */
void ptr13() {
    int i = 100;
    int *pInt = &i;
    int **ppInt = &pInt;
    int ***pppInt = &ppInt;
    printf("i=%d\n", i);
    printf("pInt=%d\n", *pInt);
    printf("ppInt=%d\n", **ppInt);
    printf("ppInt=%d\n", ***pppInt);

    char *result = NULL;//普通指针作为一个结果
    find("mother fucker", 'o', &result);
    if (result) {
        printf("result addr=%p\n", result);
        printf("result value=%c\n", *result);
        printf("result value=%s\n", result);
    }
}

void ptr14() {
    int array[] = {1, 2, 3, 4, 5};//c语言不报数组越界
    printf("size = %lu\n", sizeof(array));
    char *a = "mother fucker";
    char b[] = "mother fucker";
    printf("size = %lu\n", sizeof(a));
    printf("size = %lu\n", sizeof(b));
    //sizeof只能针对数组引用，不能针对指针引用，因为指针指向的是一个常量，而数组则是一个数组

    char *x = "aifdsf";
    char *y = "fafsf";

    while (*x) {
        printf("%c\n", *x);
        x++;
    }
}


/**
 指针数组和结构体
 */
void ptrsome() {
    struct DDD {
        char f;
        char u;
        char c;
        char k;
        int num;
    };
    struct DDD *ptr_ddd[5];
    struct DDD d;//一个结构体
    for (int i = 0; i < 5; i++) {

        ptr_ddd[i] = &d;
    }


}